﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using TianShu.Auditing;
using TianShu.Domain;
using TianShu.Domain.Entities;
using TianShu.Reposition;
using TianShu.SqlSugarCore.Repositories;
using TianShu.SqlSugarCore.Services;
using TianShu.SqlSugarCore.SqlSugarFactory;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TianShu.MultiTenancy;
using Microsoft.Extensions.Options;
using TianShu.Json;
using TianShu.Domain.Shared;

namespace TianShu.SqlSugarCore.Repositories
{
    public class SqlSugarCoreRepository<TEntity> : DalService, ISqlSugarCoreRepository<TEntity>
        where TEntity : class, IEntity, new()
    {
        public IAuditPropertySetter AuditPropertySetter => LazyServiceProvider.LazyGetRequiredService<IAuditPropertySetter>();
        public IJsonSerializer _jsonSerializer => LazyServiceProvider.LazyGetRequiredService<IJsonSerializer>();

        /// <summary>
        /// 实体属性名称集合
        /// </summary>
        public List<string> EntityPropertyNames { get; set; } = new List<string>();

        /// <summary>
        /// 初始化实体属性名称集合
        /// </summary>
        protected void InitEntityPropertyNames()
        {
            foreach (var item in EntityAssemblyStaticSetting.Assemblies)
            {
                EntityHelper.AppendEntityTypeDic(item);
            }

            if (EntityPropertyNames == null || EntityPropertyNames.Count < 1)
            {
                EntityPropertyNames = EntityHelper.GetEntityProperties(typeof(TEntity).Name);
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="sqlSugarClientFactory"></param>
        /// <param name="httpContextAccessor"></param>
        public SqlSugarCoreRepository() : base()
        {
            InitEntityPropertyNames();
        }


        public async Task DeleteAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var list = await GetListAsync(predicate);
            await DeleteManyAsync(list, autoSave, cancellationToken);
        }

        public async Task DeleteAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await DeleteManyAsync(new List<TEntity> {entity}, autoSave, cancellationToken);
        }

        public async Task DeleteManyAsync([NotNull] IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
            {
                foreach (var item in entities)
                {
                    AuditPropertySetter.SetDeletionProperties(item);
                }

                await Db.Updateable<TEntity>(entities).ExecuteCommandAsync();
                return;
            }

            await Db.Deleteable<TEntity>(entities).ExecuteCommandAsync();
        }

        public async Task<TEntity> FindAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool includeDetails = true,
            CancellationToken cancellationToken = default)
        {
            return await GetAll<TEntity>().FirstAsync(predicate);
        }


        public async Task<TEntity> GetAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool includeDetails = true,
            CancellationToken cancellationToken = default)
        {
            var entity = await FindAsync(predicate);
            if (entity == null)
            {
                throw new UserFriendlyException($"没有符合条件的数据");
            }

            return entity;
        }


        public async Task<long> GetCountAsync(Expression<Func<TEntity, bool>> expression = null, CancellationToken cancellationToken = default)
        {
            return await GetAll<TEntity>().CountAsync(expression);
        }

        public async Task<IEnumerable<TEntity>> GetListAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool includeDetails = false,
            CancellationToken cancellationToken = default)
        {
            return await GetAll<TEntity>().Where(predicate).ToListAsync();
        }

        public async Task<IEnumerable<TEntity>> GetListAsync([NotNull] Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> sortExpression, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            return await GetAll<TEntity>().Where(predicate).OrderBy(sortExpression).ToListAsync();
        }

        public async Task<(IEnumerable<TEntity>, long)> GetListAsync(List<UrlParams> urlParams, Expression<Func<TEntity, bool>> expression = null,
            CancellationToken cancellationToken = default)
        {
            List<string> list = EntityPropertyNames;
            IDictionary<string, object> pairs = null;
            //获取查询条件
            var condition = urlParams.GetCondition(CurrentTenant, list, ref pairs);

            var query = Db.Queryable<TEntity>().Where(condition.ConditionalModels).OrderBy(condition.OrderCondition);
            if (expression != null)
            {
                query = query.Where(expression);
            }

            var result = await query.ToPageListAsync(condition.Page);
            return (result, condition.Page == null ? 0 : condition.Page.Count);
        }

        public async Task<IEnumerable<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting,
            Expression<Func<TEntity, bool>> expression = null, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            var query = GetAll<TEntity>().OrderBy(sorting);
            var page = new PageDTO()
            {
                PageIndex = skipCount / maxResultCount,
                PageDataCount = maxResultCount,
            };
            var list = await query.ToPageListAsync(page);
            return list;
        }

        public async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await InsertManyAsync(new List<TEntity> {entity}, autoSave, cancellationToken);
            return entity;
        }

        public async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if (entities == null || entities.Count() < 1)
            {
                return;
            }

            var isCreationAuditedResult = typeof(ICreationAuditedObject).IsAssignableFrom(typeof(TEntity));
            var isModificationAuditedResult = typeof(IModificationAuditedObject).IsAssignableFrom(typeof(TEntity));
            var isMultiTenantResult = typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity));
            var isCreationNameResult = typeof(ICreationNameObject).IsAssignableFrom(typeof(TEntity));
            foreach (var entity in entities)
            {
                if (isMultiTenantResult)
                {
                    var multiTenant = (IMultiTenant) entity;
                    multiTenant.TenantId = CurrentTenant.Id;
                }

                if (isCreationAuditedResult)
                {
                    AuditPropertySetter.SetCreationProperties(entity);
                }

                if (isModificationAuditedResult)
                {
                    AuditPropertySetter.SetModificationProperties(entity);
                }

                if (isCreationNameResult)
                {
                    if (entity is ICreationNameObject objectWithCreationNameObject)
                    {
                        objectWithCreationNameObject.CreationName = CurrentUser.UserName;
                    }
                }

                if (entity is IEntity<Guid> entityWithGuidId && (entityWithGuidId.Id == default || entityWithGuidId.Id == Guid.Empty))
                {
                    EntityHelper.TrySetId(entityWithGuidId, () => GuidGenerator.Create());
                }
            }

            await Db.Insertable<TEntity>(entities).ExecuteCommandAsync();
        }

        public async Task<TEntity> UpdateAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await UpdateManyAsync(new List<TEntity> {entity});
            return entity;
        }

        public async Task UpdateManyAsync([NotNull] IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var isModificationAuditedResult = typeof(IModificationAuditedObject).IsAssignableFrom(typeof(TEntity));
            foreach (var entity in entities)
            {
                if (isModificationAuditedResult)
                {
                    AuditPropertySetter.SetModificationProperties(entity);
                }
            }

            List<string> ignoreColumnList = new List<string>();
            if (typeof(ICreationAuditedObject).IsAssignableFrom(typeof(TEntity)))
            {
                ignoreColumnList.Add(nameof(ICreationAuditedObject.CreationTime));
                ignoreColumnList.Add(nameof(ICreationAuditedObject.CreatorId));
            }

            if (typeof(IDeletionAuditedObject).IsAssignableFrom(typeof(TEntity)))
            {
                ignoreColumnList.Add(nameof(IDeletionAuditedObject.DeletionTime));
                ignoreColumnList.Add(nameof(IDeletionAuditedObject.DeleterId));
            }

            if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity)))
            {
                ignoreColumnList.Add(nameof(IMultiTenant.TenantId));
            }

            if (typeof(ICreationNameObject).IsAssignableFrom(typeof(TEntity)))
            {
                ignoreColumnList.Add(nameof(ICreationNameObject.CreationName));
            }

            await Db.Updateable<TEntity>(entities).IgnoreColumns(ignoreColumnList.ToArray()).ExecuteCommandAsync();
        }

        public Guid TrySetId(TEntity entity)
        {
            if (entity is IEntity<Guid> entityWithGuidId)
            {
                EntityHelper.TrySetId(entityWithGuidId, () => GuidGenerator.Create());
                return entityWithGuidId.Id;
            }

            return default;
        }

        public Guid TrySetId(TEntity entity, Guid id)
        {
            if (entity is IEntity<Guid> entityWithGuidId)
            {
                EntityHelper.TrySetId(entityWithGuidId, () => id);
                return entityWithGuidId.Id;
            }

            return default;
        }

        public async Task<IDictionary<string, object>> GetDbDictionaryAsync(Expression<Func<TEntity, bool>> whereExpression,
            Expression<Func<TEntity, object>> keyExpression, Expression<Func<TEntity, object>> resultExpression)
        {
            return await GetAll<TEntity>().Where(whereExpression).ToDictionaryAsync(keyExpression, resultExpression);
        }

        public async Task<IDictionary<TKey, TResult>> GetDictionaryAsync<TKey, TResult>(Expression<Func<TEntity, bool>> whereExpression,
            Func<TEntity, TKey> keyExpression, Func<TEntity, TResult> resultExpression)
        {
            return (await GetAll<TEntity>().Where(whereExpression).ToListAsync()).ToDictionary(keyExpression, resultExpression);
        }

        public async Task<TResult> FindAsync<TResult>(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TResult>> selectExpression,
            Expression<Func<TEntity, object>> sortExpression = null)
        {
            var query = GetAll<TEntity>().Where(whereExpression);
            if (sortExpression != null)
            {
                query = query.OrderBy(sortExpression);
            }

            return await query.Select(selectExpression).FirstAsync();
        }
    }


    public class SqlSugarCoreRepository<TEntity, TKey> : SqlSugarCoreRepository<TEntity>,
        ISqlSugarCoreRepository<TEntity, TKey>
        where TEntity : class, IEntity<TKey>, new()
    {
        public SqlSugarCoreRepository() : base()
        {
        }

        public async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await DeleteAsync(e => SqlFunc.Equals(e.Id, id));
        }

        public async Task DeleteManyAsync([NotNull] IEnumerable<TKey> ids, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await DeleteAsync(e => SqlFunc.ContainsArray(ids.ToArray(), e.Id));
        }

        public async Task<TEntity> GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            var entity = await GetAsync(e => e.Id.Equals(id));
            return entity;
        }

        public async Task<TEntity> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            var entity = await FindAsync(e => SqlFunc.Equals(id, e.Id));
            return entity;
        }
    }
}